<template>
  <div class="container">
    <template>
      <div v-if="mode === 'edit'">
        <el-select
          v-model="native_voice_name"
          placeholder="请选择"
        >
          <el-option
            v-for="item in notifyOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          >
          </el-option>
        </el-select>
        <div style="margin-top:10px">
          <!-- 文本： -->
          <el-input
            v-model="text"
            placeholder="请输入要合成的文本"
            @input="handleInput"
            clearable
          ></el-input>
        </div>
        <el-button
          class="synthesis-btn"
          type="primary"
          @click="voiceSynthesis"
          :disabled="synthesisDisabled"
          :loading="synthesisLoading"
        >
          {{ btnText }}
        </el-button>
        <el-row v-if="audioSrc">
          <audio
            :src="audioSrc"
            controls="controls"
          ></audio>
        </el-row>
      </div>
      <div v-else>{{ value }}</div>
    </template>
  </div>
</template>

<script>
import axios from "axios"
import customRequest from "../../mixins/custom-request.js"
import { Base64 } from 'js-base64'
import CryptoJS from "crypto-js"
export default {
  name: "voiceSynthesis",
  components: {},
  mixins: [customRequest],
  props: {
    value: {},
    mode: {
      default: "edit", // 编辑模式和 edit， 展示模式 view
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    // url: {
    //   type: String,
    //   default: "",
    // },
    // notifyUrl: {
    //   type: String,
    //   default: "",
    // },
    // token: {
    //   type: String,
    //   default: "",
    // },
    // paasid: {
    //   type: String,
    //   default: "",
    // },
  },
  data() {
    return {
      btnText: "语音合成",
      text: "",
      notifyOptions: [],
      synthesisLoading: false,
      audioSrc: null,
      group: '语音合成',
      token: null,
      paasid: null,
      url: null,
    };
  },

  watch: {},
  created() {
    this.requestVoiceSynth().then((res) => {
      const notifyUrl = this.getCodeValueByCode('ON_NOTIFY_URL', res)
      this.token = this.getCodeValueByCode('VOICE_RECOGNITION_TOKEN', res)
      this.paasid = this.getCodeValueByCode('VOICE_RECOGNITION_PAASID', res)
      this.url = this.getCodeValueByCode('VOICE_RECOGNITION_URL', res)
       if (notifyUrl) this.getNotify(notifyUrl)
    })
  },
  mounted() {
    if (this.value) {
      if (this.mode == "view") {
        this.disabled = true;
      }
    }
  },
  computed: {
    synthesisDisabled() {
      return this.disabled || _.isEmpty(this.text);
    },
  },
  methods: {
    handleInput() {
      this.disabled = false
      this.audioSrc = null
    },
    getCommonParams() {
      const { token, paasid } = this;
      const headers = this.header
      const timestamp = (Date.now() / 1000).toFixed();
      const nonce = Math.random().toString(36).substring(2);
      const signature = CryptoJS.SHA256(timestamp + token + nonce + timestamp).toString(CryptoJS.enc.Hex).toUpperCase();
      return { headers, timestamp, nonce, signature, paasid }
    },
    getNotify(url) {
      const requestUrl = `${this.preurl}api/afc/integration/onNotify`
      const param = this.getCommonParams()
      const { headers, timestamp, nonce, signature, paasid } = param
      // const { notifyUrl } = this;
      const type = "voiceInfo";
      const sParam = "";
      const bParam = null;
      const payload = { url, paasid, timestamp, nonce, signature, type, sParam, bParam };
      axios.post(`${requestUrl}`, payload, { headers }).then((resp) => {
        try {
          const { sResult } = resp.data;
          const result = JSON.parse(sResult);
          const options = []
          for (let item of result) {
            const { name } = item
            options.push({ label: name, value: name })
          }
          this.notifyOptions = options
          this.native_voice_name = options[0].value
        } catch (err) {
          console.log(err)
        }
      }).catch((err) => {
        console.log("请求onNotify接口错误:", err)
        const message = _.get(err, "response.data.errorLocalizedMessage")
        this.$message.error(message || err.message)
        this.disabled = true
      })
    },
    voiceSynthesis() {
      if (_.isEmpty(this.text)) return
      this.synthesisLoading = true;
      const requestUrl = `${this.preurl}api/afc/integration/createRec`
      const param = this.getCommonParams()
      const { headers, timestamp, nonce, signature, paasid } = param
      const url = this.url
      let { native_voice_name, text } = this;
      if (_.isEmpty(native_voice_name)) {
        native_voice_name = null
      }
      const audio_coding = "raw";
      const sample_rate = "16000";
      const volume = "0";
      const speed = "0";
      const pitch = "0";
      const payload = { url, paasid, timestamp, nonce, signature, native_voice_name, text, audio_coding, sample_rate, volume, speed, pitch };
      axios.post(`${requestUrl}`, payload, { headers }).then((resp) => {
        const { data } = resp
        this.audioSrc = this.pcmtoWav(data)
        this.disabled = true
      }).catch((err) => {
        console.log("请求语音合成接口错误:", err)
        const message = _.get(err, "response.data.errorLocalizedMessage")
        this.$message.error(message || err.message)
      }).finally(() => {
        this.synthesisLoading = false;
      })
    },
    pcmtoWav(pcmsrt, sampleRate, numChannels, bitsPerSample) {
      //参数->（base64编码的pcm流，采样频率，声道数，采样位数）
      let header = {
        chunkId: [0x52, 0x49, 0x46, 0x46],
        chunkSize: 0,
        format: [0x57, 0x41, 0x56, 0x45],
        subChunk1Id: [0x66, 0x6d, 0x74, 0x20],
        subChunk1Size: 16,
        audioFormat: 1,
        numChannels: numChannels || 1,
        sampleRate: sampleRate || 16000,
        byteRate: 0,
        blockAlign: 0,
        bitsPerSample: bitsPerSample || 16,
        subChunk2Id: [0x64, 0x61, 0x74, 0x61],
        subChunk2Size: 0,
      }
      let pcm = Base64.toUint8Array(pcmsrt)
      header.blockAlign = (header.numChannels * header.bitsPerSample) >> 3
      header.byteRate = header.blockAlign * header.sampleRate
      header.subChunk2Size = pcm.length * (header.bitsPerSample >> 3)
      header.chunkSize = 36 + header.subChunk2Size
      let wavHeader = header.chunkId.concat(
        this.u32ToArray(header.chunkSize),
        header.format,
        header.subChunk1Id,
        this.u32ToArray(header.subChunk1Size),
        this.u16ToArray(header.audioFormat),
        this.u16ToArray(header.numChannels),
        this.u32ToArray(header.sampleRate),
        this.u32ToArray(header.byteRate),
        this.u16ToArray(header.blockAlign),
        this.u16ToArray(header.bitsPerSample),
        header.subChunk2Id,
        this.u32ToArray(header.subChunk2Size)
      )
      let wavHeaderUnit8 = new Uint8Array(wavHeader)
      let mergedArray = new Uint8Array(wavHeaderUnit8.length + pcm.length)
      mergedArray.set(wavHeaderUnit8)
      mergedArray.set(pcm, wavHeaderUnit8.length)
      let blob = new Blob([mergedArray], { type: 'audio/wav' })
      let blobUrl = window.URL.createObjectURL(blob)
      return blobUrl
    },
    u32ToArray(i) {
      return [i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff, (i >> 24) & 0xff]
    },
    u16ToArray(i) {
      return [i & 0xff, (i >> 8) & 0xff]
    },
  },
};
</script>
<style lang="scss" scoped>
.container {
  width: 100%;
}
.synthesis-btn {
  margin: 10px 0;
}
</style>
